Django为每个模型字段设置隐私选项

时间:2017-02-18 13:22:06

标签: python django postgresql privacy

我已经解决了这个问题,best way to implement privacy on each field in model django并且它的答案似乎没有解决我的问题所以我在这里问一些相关的问题,

好吧,我有一个用户模型。我希望用户能够控制其个人资料中每个字段的隐私(可能是gendereducationinterests等。)。

隐私选项不得仅限于私人或公开,而应仅限于

  • public
  • 朋友
  • 只有我
  • 朋友列表1(User.friendlist.one)
  • 朋友列表2(User.friendlist.two)
  • 朋友列表3(User.friendlist.three)
  • 用户可以创建的另一个infinte列表。

我也不希望将这些隐私选项保存在另一个模型上,但是相同,以便通过一个查询我可以获得用户对象以及隐私选项。

所以如果我有UserModel,

class User(models.Model):
    name = models.CharField()
    email = models.EmailField()
    phone = models.CharField()

如何在此处设置隐私设置?我正在使用postgres,我可以映射JSON字段或Hstore甚至是ArrayField吗?

人们过去常常与Django做同样问题的解决方案是什么?

更新

我有n个模型字段。我真正想要的是将每个实例的隐私设置存储在自身或其他方便的方式。

5 个答案:

答案 0 :(得分:8)

我已经解决了我的问题,尝试了具有权限和其他关系的解决方案。我有一个 Relationship 模型,所有其他关系列表都来自Relationship模型,所以我不想维护一个单独的关系列表。

所以我的选择是使用Postgres JSONField或HStoreField。由于Django对postgres freatures有很好的支持,我发现这些点是我所做出的选择。

  • 可以使用Django ORM查询JSON / HashStore。
  • 配置是普通的JSON / HashStore,易于编辑和维护,而不是权限和关系。
  • 我发现使用权限的数据库查询时间比使用JSON / HStore要大。 (点击率更高,有权限)
  • 添加和验证每个字段的权限复杂比添加/验证JSON。
  • 在未来的某个时候如果有一个更简单或更轻松的解决方案,我可以在一个字段中迁移到整个配置。

所以我的选择是使用配置模型。

class UserConfiguration(models.Model):
    user = # link to the user model
    configuration = #either an HStore of JSONFeild

然后编写了一个验证器,以确保在保存和更新时不会弄乱配置数据模型。我将字段分组以最小化验证字段。然后编写了一个简单的解析器,它接受用户并找到它们之间的关系,然后使用配置映射以返回允许的字段数据(在未经优化的实现中以2-4ms记录,现在已足够)。 (有了权限,我需要一个单独的朋友列表来维护,并且应该更新所有关于更新隐私配置的组权限,然后我仍然需要验证权限并处理它,这可能比这更短的时间,但是对于复杂系统的成本)。

我认为这种方法也是可扩展的,因为大多数处理是在Python中完成的,数据库调用尽可能减少。

<强>更新

我进一步削减了数据库查询。在之前的实现中,迭代的用户之间的关系(定时大约1-2ms,将此实现更改为.value_list('relations', flat=True)将查询时间减少到400-520μs。

答案 1 :(得分:7)

  

我也不希望将这些隐私选项保存在另一个模型上,但是相同,以便通过一个查询我可以获得用户对象以及隐私选项。

我建议您将隐私对象与UserModel分离,以免将用户数据与这些选项混为一谈。要最大限度地减少数据库查询的数量,请使用djangos select_relatedprefetch_related

您定义的要求IMO会导致一组与隐私相关的对象绑定到UserModel。在这种情况下,django.contrib.auth是一个很好的开始。它是可扩展的构建。阅读有关该主题的the docs

如果您希望有大量用户,因此也需要更多的组,您可能需要考虑在基于redis的会话中编写为一个用户解析的权限,以便能够在每次加载页面时快速获取它们。 / p>

<强>更新

我更多地考虑了您的要求,并得出结论:您需要django-guardian中实现的每个对象权限。您应该首先开始阅读他们的样本代码。他们在django.contrib.auth之上构建它,但不依赖于它,这使得它也可以用于遵循django.contrib.auth中的接口的自定义实现。

答案 2 :(得分:5)

这样的事情怎么样?

class EditorList(models.Model):
    name = models.CharField(...)
    user = models.ForeignKey(User)
    editor = models.ManyToManyField(User)

class UserPermission(models.Model):
    user = models.ForeignKey(User)
    name = models.BooleanField(default=False)
    email = models.BooleanField(default=False)
    phone = models.BooleanField(default=False)
    ...
    editor = models.ManyToManyField(User)
    editor_list = models.ManyToManyField(EditorList)

如果用户想要向public提供“电子邮件”权限,那么她会创建一个UserPermission editor=Noneeditor_list=None以及email=True

如果她想允许用户“rivadiz”编辑她的电子邮件,那么她会使用UserPermissioneditor='rivadiz'创建email=True

如果她想要创建可以编辑手机的朋友列表,那么她会创建并填充名为“my_friends”的EditorList,然后使用UserPermission创建editor_list='my_friends'并{ {1}}

然后,您应该能够查询所有有权编辑任何用户的任何字段的用户。 您可以在phone=True模型中定义一些属性,以便在UserUser的情况下轻松检查哪些字段是可编辑的。 您首先需要获得编辑所属的所有editor,然后执行类似

的操作

EditorLists

答案 3 :(得分:5)

如果没有单独的权限模型,这将更加麻烦。您可以将单个用户配置文件的给定字段与多个朋友列表相关联,这意味着可以使用“多对多”表,而您最好只让Django为您处理。

我在想更多的东西:

class Visibility(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    field = models.CharField(max_length=32)
    public = models.BooleanField(default=False)
    friends = models.BooleanField(default=False)
    lists = models.ManyToManyField(FriendList)

    @staticmethod
    def visible_profile(request_user, profile_user):
        """Get a dictionary of profile_user's profile, as
           should be visible to request_user..."""

(我会把这种方法的细节留作练习,但事实并非如此 太复杂了。)

我要提醒一下,由于与朋友列表的多对多连接,用户设置这些权限所涉及的UI可能是一个挑战。绝对不是不可能,但有点单调乏味。

M2M表的一个关键优势是,如果用户或任何朋友列表被删除,它将自我维护 - 只有一个例外。这个方案中的想法是,如果没有任何可见性记录,所有数据都是私有的(为了让每个人都能看到你的名字,你要添加一个可见性记录,其中user =(你自己),field =“name”和public = True。由于可见性记录中public = False,friends = False,而lists = []是没有意义的,我会在用户编辑后检查该情况并完全删除该记录。

另一个有效的策略是拥有两个特殊的FriendList记录:一个用于“公共”,一个用于“所有朋友”。这在很大程度上简化了可见性模型,但代之以其他地方的代码更多。

答案 4 :(得分:5)

首先,在我看来,你应该选择多个模型并提高查询速度,如其他答案中已经提到的,你可以根据你的用例使用缓存或select_related或prefetch_related。

所以这是我提出的解决方案:

用户模型

class User(models.Model):
    name = models.CharField()
    email = models.EmailField()
    phone = models.CharField()
    ...
    public_allowed_read_fields = ArrayField(models.IntegerField())
    friends_allowed_read_fields = ArrayField(models.IntegerField())
    me_allowed_read_fields = ArrayField(models.IntegerField())
    friends = models.ManyToManyField(User)
    part_of = models.ManyToManyField(Group, through=GroupPrivacy)

组(朋友列表)模型

class Group(models.Model):
    name = models.CharField()

通过模型

class GroupPrivacy(models.Model):
    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)
    allowed_read_fields = ArrayField(models.IntegerField())

用户模型字段映射到整数

USER_FIELDS_MAPPING = (
    (1, User._meta.get_field('name')),
    (2, User._meta.get_field('email')),
    (3, User._meta.get_field('phone')),
    ...
)

这有什么用?

  • 对于publicfriendsme中的每一个,您可以在用户模型中拥有一个字段,如上所述,public_allowed_read_fields,{{分别为1}}和friends_allowed_read_fields。每个字段都将包含一个映射到USER_FIELDS_MAPPING内部的整数列表(详见下文)

  • 对于me_allowed_read_fields,您将拥有名为friend_list_1的群组。现在重点是用户想要向这个朋友列表显示或隐藏一组特定字段。这就是friend_list_1模型, GroupPrivacy 进入游戏的地方。使用此直通模型可以定义用户与组之间的M2M关系,并具有此关系所特有的一些附加属性。在此 GroupPrivacy 模型中,您可以看到through字段,它用于存储与 USER_FIELDS_MAPPING 中的整数对应的整数数组。我们可以说,对于小组allowed_read_fields和用户friend_list_1A = [1,2]。现在,如果您将此映射到USER_FIELDS_MAPPING,您将知道用户allowed_read_fields只想向此列表中的朋友显示姓名和电子邮件。同样,A组中的不同用户在friend_list_1中对应的 GroupPrivacy 模型实例具有不同的值。

对于多个群体,情况类似。