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

Riv*_*diz 12 python django postgresql privacy

我已经解决了问题,在模型django中每个字段实现隐私的最佳方法,它的答案似乎没有解决我的问题,所以我在这里问一些相关的问题,

好吧,我有一个用户模型.我希望用户做出能够控制每个的隐私和个人资料的各个领域(可能是 gender,education,interests等..).

隐私选项不得仅限于私人或公共,而是描述为

  • 上市
  • 朋友
  • 只有我
  • 朋友列表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()
Run Code Online (Sandbox Code Playgroud)

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

人们过去常常使用Django解决同样问题的最佳解决方案是什么?

更新:

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

Riv*_*diz 9

我已经解决了我的问题,尝试了具有权限和其他关系的解决方案.我有一个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
Run Code Online (Sandbox Code Playgroud)

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

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

更新

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


dah*_*ens 7

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

我建议你将隐私对象UserModel与这些选项分离,以免将用户数据与这些选项混为一谈.要最小化数据库查询量,请使用djangos select_relatedprefetch_related.

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

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

更新:

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


Rob*_*b L 6

这样的事情怎么样?

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)
Run Code Online (Sandbox Code Playgroud)

如果用户想给"电子邮件"的权限public,然后她创建了一个UserPermissioneditor=Noneeditor_list=Noneemail=True.

如果她想让用户"rivadiz"编辑她的电子邮件,那么她会创建一个UserPermissionwith editor='rivadiz'email=True.

如果她想创建一个可以编辑她的手机的朋友列表,那么她创建并填充一个EditorList名为'my_friends'的人,然后创建一个UserPermissionwith editor_list='my_friends'phone=True

然后,您应该能够查询所有有权编辑任何用户的任何字段的用户.您可以在User模型中定义一些属性,以便在给定a User和a的情况下轻松检查哪些字段是可编辑的editor.你首先需要得到EditorLists一个属于的编辑器,然后做类似的事情

perms = UserPermissions.objects.filter(user=self).filter(Q(editor=editor) | Q(editor_list=editor_list))


Rus*_*ter 6

没有单独的权限模型,这将更加麻烦.您可以将单个用户配置文件的给定字段与多个朋友列表相关联,这意味着可以使用"多对多"表,而您最好只让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..."""
Run Code Online (Sandbox Code Playgroud)

(我会把这种方法的细节留作练习,但它并不太复杂.)

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

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

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


小智 6

首先,在我看来,你应该选择多个模型并使查询更快,正如其他答案中已经提到的,你可以根据你的用例使用缓存或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)
Run Code Online (Sandbox Code Playgroud)

组(朋友列表)模型

class Group(models.Model):
    name = models.CharField()
Run Code Online (Sandbox Code Playgroud)

通过模型

class GroupPrivacy(models.Model):
    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)
    allowed_read_fields = ArrayField(models.IntegerField())
Run Code Online (Sandbox Code Playgroud)

用户模型字段映射到整数

USER_FIELDS_MAPPING = (
    (1, User._meta.get_field('name')),
    (2, User._meta.get_field('email')),
    (3, User._meta.get_field('phone')),
    ...
)
Run Code Online (Sandbox Code Playgroud)

这有什么用?

  • 每个的public,friends并且me,你可以在用户模式本身已经提到以上即场public_allowed_read_fields,friends_allowed_read_fieldsme_allowed_read_fields分别.每个字段都将包含一个映射到USER_FIELDS_MAPPING内部的整数列表(详见下文)

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

对于多个组,这将是类似的.