Django是一个受欢迎的基于Python的Web框架。这是一个庞大的所谓电池框架,涵盖了Web开发的许多方面:身份验证,ORM,形式,管理面板等。这也是一个强烈的自以为是的框架,可为几乎所有内容提供模式您做到了,使您在开发过程中感觉很好。
但是,在过去的几年中,Django像大多数非JS堆栈一样,正在为基于JS的全堆栈框架(例如Next.js,Remix,nuxt等)失去理由。为什么?以下帖子给出了一个很好的解释:
从一个框架转到另一个框架需要仔细的计划和执行,尤其是当您同时更改语言时。 Django的流行且功能强大的JavaScript/Typescript等效堆栈可以是以下组合:
- Next.js:用ReactJ(Django的视图 +模板层) 的URL路由,SSR和页面构建
- NextAuth:身份验证(Django的身份验证)
- Prisma:ORM +数据库迁移(Django的模型层)
这些碎片非常适合融合在一起,足以代替Django提供的大多数好东西。但是,缺少一块。 Django具有内置的权限功能,但仅限于模型级控件,即,如果用户或组具有X访问模型Y的访问。许多用户一直使用流行的django-guardian软件包来实现行级权限。它允许您在用户/组和对象之间建立权限,管理基础权限数据库表,并提供用于配置和检查此类权限的API。
幸运的是,如果您选择在新堆栈中使用Prisma Orm,则可以使用ZenStack在减少努力的情况下实现类似的功能。 Zenstack是一种工具包,作为Prisma Orm的功率扩展,其重点之一是访问控制。这篇文章简要比较了django-guardian和Zenstack分别如何求解行级权限。
分配权限
假设我们正在建立一个博客网站并具有Post
型号。 Django已经具有内置的User
和Group
型号,并为每个模型提供了预定义的CRUD权限。使用Django-Guardian,您可以使用assign_perm
API附加权限:
from django.contrib.auth.models import User, Group
from guardian.shortcuts import assign_perm
# establishing permission between a user and a post
user1 = User.objects.create(username='user1')
post1 = Post.object.create(title='My Post', slug='post1')
assign_perm('view_post', user1, post1)
assign_perm('change_post', user1, post1)
# establishing permission between a group and a post
group1 = Group.objects.create(name='group1')
user1.groups.add(group1)
assign_perm('view_post', group1, post1)
assign_perm('change_post', group1, post1)
与Django相反,Next.js + Prisma + Zenstack是一个未开放的框架,并且没有User
和Group
的内置模型。您需要使用Zenstack架构明确对它们进行建模:
model User {
id Int @id @default(autoincrement())
username String
groups Group[]
}
model Group {
id Int @id @default(autoincrement())
name String
users User[]
}
模型不仅建模数据类型和关系,而且还允许您在其中表达权限。让我们看看如何在Post
上建模User
和Group
的权限:
model User {
id Int @id @default(autoincrement())
username String
groups Group[]
posts Post[]
}
model Group {
id Int @id @default(autoincrement())
name String
users User[]
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
slug String @unique
groups Group[]
users User[]
// if the current user is in the user list of the post, update is allowed
@@allow('read,update', users?[id == auth().id])
// if the current user is in any group of the group list of the post,
// update is allowed
@@allow('read,update', groups?[users?[id == auth().id]])
// ... other permissions
}
一些解释:
-
@@allow
语法将访问控制元数据添加到模型中。如果@@允许规则评估为true,则允许采取行动。 -
auth()
代表当前的登录用户。您会看到它很快就被吸引了。 - 模型?[表达式]语法是对一个人关系的谓词。
users?[id == auth().id]
示为“用户集合中的任何元素是否具有与当前用户的ID相等的ID”。
您可以看到,Django-Guardian和Zenstack的权限建模方法完全不同。 Django-Guardian使用命令式代码来管理应用程序代码的权限,而Zenstack则偏向于数据架构中的声明样式。另外,Django-Guardian的权限设置和检查(如下一部分显示)是分开的,而使用Zenstack,您可以将许可数据和规则完全建模。
检查权限
与许可分配一样,使用Django-Guardian,在应用程序代码中也明确完成了许可检查,主要以两种方式之一:
1。使用命令式代码
user1 = User.objects.get(username='user1')
post1 = Post.objects.get(slug='post1')
from guardian.core import ObjectPermissionChecker
checker = ObjectPermissionChecker(user1)
if checker.has_perm('change_post', post1):
# update logic here
2。使用装饰
您还可以使用装饰器启用自动许可检查视图:
from guardian.decorators import permission_required_or_403
@permission_required_or_403('change_post', (Post, 'slug', 'post_slug'))
def edit_post(request, post_slug):
# update logic here
无论您使用哪种方法,都必须确保在需要验证权限的地方添加检查。
使用Zenstack,权限检查要简单得多,因为规则在ORM级别表示,因此在调用数据层时会自动应用它:
- 当列出帖子时,仅返回属于当前用户或他的组的帖子。
- 更新帖子时,如果用户不属于帖子或任何帖子的任何组,则将拒绝操作。
唯一需要的设置是用当前登录用户作为上下文创建一个启用访问控制的PRISMA客户端包装器:
// update-post.ts: function for updating a post
import { prisma } from './db';
import { getSessionUser } from './auth';
export function updatePost(request: Request, slug: string, data: PostUpdateInput) {
const user = await getSessionUser(req);
// get an access-control enabled Prisma wrapper
// the "user" context value supports the `auth()`
// function in the permission rules
const db = withPresets(prisma, { user });
// error will be thrown if the current user doesn't
// have permission
return db.post.update({ where: { slug }, data });
}
包起来
您可以看到,尽管以截然不同的范式,但django-guardian和Zenstack都解决了行级的权限问题。 Django-Guardian依赖于命令式代码,而更多的是开发人员的责任,以确保在需要时添加适当的检查逻辑。
另一方面,Zenstack有利于声明的建模。由于规则是在ORM级别表达的,因此它会自动适用于使用数据层的所有应用程序代码,从而提高一致性和鲁棒性。
作为一个相对较新的工具包,Zenstack并非没有其局限性。例如,与django-guardian相比,有两个主要缺失功能:
-
自定义权限
Zenstack模型固定的权限集:CRUD,而Django-Guardian允许您定义自定义权限。尽管所有权限最终都归结为CRUD,但自定义权限可以表达精细的野外许可控制。 Zenstack还不支持这一点。
-
API明确检查权限
Zenstack的权限检查被注入ORM的CRUD API。但是,有时明确检查用户是否有特定对象的权限并将其动态渲染UI。
我希望这篇文章为您将Python堆栈过渡到JavaScript世界的旅程提供了一些帮助,并祝您好运!