根据用户访问级别隐藏模板中的关键数据



假设我有一个Item模型,其中Item对象可以是public(所有用户都可以访问(或private(只有经过身份验证的用户才能访问(:

class Item(models.Model):
title = models.CharField(max_length=100)
is_public = models.BoleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
#...
secret_key = ...
class Meta:
# I need to keep items in order:
ordering = ('-created_at',)

我需要的是使用generic.ListView列出所有项目 - 保持顺序 - 但隐藏这些项目的secret_keyis_public=False匿名用户。

因此,在模板中,如果用户未经过身份验证,我将隐藏secret_key,例如:

{% if request.user.is_authenticated %}
<p>{{ item.title }} - {{ item.secret_key }}</p>
{% else %}
<p>{{ item.title }} - This item is private. Sign up to see the secret_key!</p>
{% endif %}

ListView是这样的:

class ItemListView(ListView):
model = Item
paginate_by = 10

我知道我可以将两个单独的查询集发送给非登录用户到模板,一个用于公共项目,另一个用于私有项目;但我不确定如何在这种方法中保持顺序('-created_at'(。

问题是:

  1. 将所有secret_keys发送到模板并为那里的非登录用户隐藏它们是否安全

  2. (如果它是安全的,那么(有没有更有效的方法?

我尝试覆盖ItemListViewget_queryset方法并将if条件从模板移动到那里(我认为这会提高性能,对吧?我处理了用户经过身份验证的情况(只需返回所有对象(;但是对于非登录用户,我想以某种方式加入两个单独的查询集,一个保存公共项目,另一个保存私有项目的titlecreated_at;但我没有发现在这种方法中保持顺序:

class ItemListView(ListView):
model = Item
paginate_by = 10
def get_queryset(self):
if self.request.user.is_authenticated:
return Item.objects.all()
else:
# ???

这只是一个最小可重现的例子;实际上在项目中,我有多个access_levels;每个用户都有一个access_level,基于他们的plan(例如基本,普通,专业等(,每个Item都有一个access_level;我正在处理大约+100K的对象,这些对象是从不同的数据库(postgresql- 一些缓存在redis上(获取的,所以性能在这里真的很重要。此外,系统现已启动并运行;所以我更喜欢不太基本的解决方案。

谢谢你的时间。非常感谢您的帮助。

将所有

secret_keys发送到模板并为那里的非登录用户隐藏它们是否安全?

您的模板在服务器端呈现,客户端仅获取呈现的标记,所以是的,它是完全安全的。好吧,当然,除非您团队中的某个人弄乱了模板代码;-(

(如果它是安全的,那么(有没有更有效的方法?

只需在视图中筛选查询集 - 不需要两个不同的查询集,筛选查询集不会更改其顺序。

def get_queryset(self):
qs = super(ItemListView, self).get_queryset()  
if not self.request.user.is_authenticated:
qs = qs.filter(is_private=False) 
return qs

在您的模板中:

{# avoids doing the same constant lookup within the loop #}
{% with is_auth=request.user.is_authenticated %}
{# I assume the queryset is named "objects" in the context ?#}
{% for item in objects %} 
<p>{{ item.title }}{% if is_auth %} - {{ item.secret_key }}{% endif %}</p>
{% endfor %}
{% endwith %}

编辑:bdoubleu在他的回答中正确地提到,他的解决方案使测试更容易。如果只需要模型中的字段(无方法调用(,也可以改用 QuerySet.values((:

def get_queryset(self):
qs = super(ItemListView, self).get_queryset()  
fields = ["title", "created_at"]
if self.request.user.is_authenticated:
fields.append("secret_key")
else:
qs = qs.filter(is_private=False) 
return qs.values(*fields)

这也将使你的代码更有效率,因为它不必构建完整的模型实例。

另一种选择是对查询集进行批注,以便为display_secret_key添加一个额外的属性,这将比在模板化时检查查询集中每个项目的用户访问级别更有效。

from django.db.models import F, Value as V
class ItemListView(ListView):
queryset = Item.objects.all()
paginate_by = 10
def get_queryset(self):
annotations = {
'display_secret_key': V('')
}
if self.request.user.access_level == 'PRO':
annotations['display_secret_key'] = F('secret_key')
return (
super().get_queryset()
.annotate(**annotations)
)

然后在模板中:

<p>{{ item.title }} - {{ item.display_secret_key }}</p>

您可以使用2 个模板,一个用于经过身份验证的用户,一个用于未经身份验证的用户。(只需覆盖 get_template_names(( 进行身份验证检查,并在找到的名称中添加类似 _sectempl.html 的内容,并使用机密数据添加模板的相应副本(

但我想和 bruno desthuilliers 说,如果您关闭调试模式,则不会有未经身份验证的用户在其中看到内容的星座

{% with authenticated=request.user.is_authenticated %}
{% if authenticated %}
do secret stuff 
{% endif %}
{% endwith %}

{% if request.user.is_authenticated %}
hide secret stuff for all the others
{% endif %}

如果你在标准的 django 用户权限管理之外有一个复杂的用户分组组合(你可以在模板中请求用户权限(,那么我会将user_status(你的"计划"或访问级别(写入用户会话(身份验证时(,并在对象属性的输出函数中检查此user_status。

草图:在模板中使用:

{% for i in object_list %}
{{ i.name}}, {{ i.print_secret }}
{% endfor %}

在模型中,您创建一个"print_secret"方法女巫根据会话数据中先前记录的user_status返回机密。

最新更新