django-rest-framework搭建平台实战教程四-使用fast-crud集成后端权限功能
FastCrud (简称fs) 是基于Vue3的面向配置的crud开发框架,快速开发crud功能,可作为低代码平台的基础框架。
安装并启动项目
参考http://fast-crud.docmirror.cn/guide/start/demo.html
我用的是fs-admin-element
.env文件修改
参考http://fast-crud.docmirror.cn/admin/#_2-%E4%BF%AE%E6%94%B9%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6
修改后端ip和端口
参考http://fast-crud.docmirror.cn/admin/#_3-%E4%BF%AE%E6%94%B9%E5%90%8E%E7%AB%AFip%E5%92%8C%E7%AB%AF%E5%8F%A3
禁用mock
参考http://fast-crud.docmirror.cn/admin/#_4%E3%80%81%E7%A6%81%E7%94%A8mock
修改登录接口
参考http://fast-crud.docmirror.cn/admin/#_6-%E4%BF%AE%E6%94%B9%E7%99%BB%E5%BD%95%E6%8E%A5%E5%8F%A3
django后端urls.py路由全部加上api/
router = routers.DefaultRouter()
router.register(r'api/users', views.DUserViewSet)
router.register(r'api/groups', views.GroupViewSet)
router.register(r'api/permission', views.PermissionViewSet)
urlpatterns = [ path('', include(router.urls)), # path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # path('admin/', admin.site.urls), path('api/register',views.register), path('api/login',views.login), path('api/sys/authority/user/mine',views.info) ]
修改/src/store/modules/user.ts
...
setToken(info: string, expire: number) { this.token = "Bearer " + info; LocalStorage.set(TOKEN_KEY, this.token, expire); },
正常就能登录系统了
角色管理页前后端对接
view/sys/authority/role/api.ts匹配后端接口
import { request } from "/src/api/service"; const apiPrefix = "/groups/"; export async function GetList(query) { return request({ url: apiPrefix, method: "get", data: query }); } export async function AddObj(obj) { return request({ url: apiPrefix, method: "post", data: obj }); } export async function UpdateObj(obj,id) { return request({ url: apiPrefix+id+'/', method: "put", data: obj }); } export async function DelObj(id) { return request({ url: apiPrefix+id+'/', method: "delete", params: { id } }); }
crud.ts改接口参数
... columns: { id: { title: "id", form: { show: false }, // 表单配置 column: { width: 70, sorter: true } }, name: { title: "角色名称", type: "text", search: { show: true }, form: { rules: [ { required: true, message: "请输入角色名称" }, { max: 50, message: "最大50个字符" } ] }, // 表单配置 column: { sorter: true } }, update_time: { title: "更新时间", type: "datetime", column: { sorter: true }, form: { show: false } // 表单配置 } }
/src/plugin/fast-crud/index.ts修改默认的创建时间字段
后端authen添加custompagination.py
from rest_framework import pagination from rest_framework.response import Response class CustomPagination(pagination.PageNumberPagination): def get_paginated_response(self, data): return Response({ 'currentPage': self.page.number, 'pageSize': self.page_size, 'total': self.page.paginator.count, 'records': data })
修改settings
... REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'djangoXfastcrud.authen.CustomPagination.CustomPagination', 'PAGE_SIZE': 10, 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] }
前端src/plugins/fast_crud/index.tsx修改全局翻页参数
...
transformQuery: ({ page, form, sort }) => { return { page: page.currentPage, ...form }; },
后端GroupViewSet添加显示权限tree视图
... @action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated]) def tree(self, request: Request): content_types = ContentType.objects.all() resdata = [] for c in content_types: permissions = Permission.objects.filter(content_type_id=c.id).all() plist = [] for p in permissions: plist.append({ "id": p.id, "parentId": c.id, "permission": p.codename, "title": p.name, }) resdata.append({ "id": -1, "parentId": -1, "permission": c.app_label, "title": c.model, "children": plist }) return Response({ "code": 0, "data": resdata })
前端api.ts添加接口
export async function GetTree() { return request({ url: apiPrefix + "tree/", method: "post" }); }
修改index.vue
async function authzOpen(roleId) { permissionTreeData.value = await api.GetTree(); #改成新的接口 ...
添加授权相关接口
class GroupViewSet(CustomViewSet): ... @action(methods=['POST'], detail=False, permission_classes=[permissions.IsAuthenticated]) def getPermissionIds(self, request: Request): group = Group.objects.filter(id=request.query_params['id']).first() ids = group.permissions.values_list("id", flat=True) return Response({ "code": 0, "data": ids }) @action(methods=['POST'], detail=False) def authz(self, request: Request): if request.user.has_perm("auth.change_group"): group = Group.objects.filter(id=request.data['roleId']).first() group.permissions.set(request.data['permissionIds']) return Response({"code": 0, "message": "success"}) else: raise PermissionDenied
前端api.ts修改url
/** * 获取角色权限资源 * @param roleId * @returns {*} * @constructor */ export function getPermissionIds(roleId) { return request({ url: apiPrefix + "getPermissionIds/", method: "post", params: { id: roleId } }); } /** * 授权 * @param roleId * @param permissionIds * @returns {*} * @constructor */ export function DoAuthz(roleId, permissionIds) { return request({ url: apiPrefix + "/authz/", method: "post", data: { roleId, permissionIds } }); }
角色页面所有功能完成
同样思路修改用户页面
api.ts
import { request } from "/src/api/service"; const apiPrefix = "/users/"; export async function GetList(query) { return request({ url: apiPrefix, method: "get", data: query }); } export async function AddObj(obj) { return request({ url: apiPrefix, method: "post", data: obj }); } export async function UpdateObj(obj) { delete obj._create_time; delete obj.update_time; return request({ url: apiPrefix+obj.id+'/', method: "put", data: obj }); } export async function DelObj(id) { return request({ url: apiPrefix+id+'/', method: "delete", params: { id } }); } export async function GetObj(id) { return request({ url: apiPrefix + "/info", method: "post", params: { id } }); }
crud.ts
... columns: { id: { title: "id", form: { show: false }, // 表单配置 column: { width: 70, sorter: true } }, username: { title: "用户名", type: "text", search: { show: true }, // 开启查询 form: { rules: [ { required: true, message: "请输入用户名" }, { max: 50, message: "最大50个字符" } ] }, editForm: { component: { disabled: true } }, column: { sorter: true } }, password: { title: "密码", type: "text", key: "password", column: { show: false }, form: { rules: [{ max: 50, message: "最大50个字符" }], component: { showPassword: true }, helper: "填写则修改密码" }, editForm: { show: false } }, nickName: { title: "昵称", type: "text", search: { show: true }, // 开启查询 form: { rules: [{ max: 50, message: "最大50个字符" }] }, column: { sorter: true } }, avatar: { title: "头像", type: "cropper-uploader", column: { width: 100, component: { //设置高度,修复操作列错位的问题 style: { height: "30px", width: "auto" } } } }, remark: { title: "备注", type: "text", column: { sorter: true }, form: { rules: [{ max: 100, message: "最大100个字符" }] } }, groups: { title: "角色", type: "dict-select", dict: dict({ url: "/groups/get_groups_dict/", value: "id", label: "name" }), // 数据字典 search: { show: true }, form: { component: { multiple: true } }, column: { width: 250, sortable: true } }, update_time: { title: "修改时间", type: "datetime", form: { show: false }, // 表单配置 column: { sortable: "update_time", width: 180 } }, }
角色选择框接口
class GroupViewSet(CustomViewSet): ... @action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated]) def get_groups_dict(self, request: Request): obj = DGroup.objects.values("id", "name") return Response({ "code": 0, "data": obj })
角色页面所有功能完成
最后解决登录获取权限的报错接口
添加视图
... @api_view(['POST']) @permission_classes([permissions.IsAuthenticated]) def getuserpermissions(request: Request): user: User = request.user plist = [] permissions = user.get_group_permissions() for p in permissions: plist.append({"permission":p}) return Response({ "code": 0, "data": plist })
urls.py添加下
path('api/sys/authority/user/permissions',views.getuserpermissions)
超级用户登录获取全部权限
添加页面权限
import LayoutPass from "/@/layout/layout-pass.vue"; export const sysResources = [ { title: "系统管理", name: "sys", path: "/sys", redirect: "/sys/authority", component: LayoutPass, meta: { icon: "ion:settings-outline", permission: "sys.sys" }, children: [ { title: "权限管理", name: "authority", path: "/sys/authority", redirect: "/sys/authority/permission", meta: { icon: "ion:ribbon-outline", //需要校验权限 permission: "sys.auth" }, children: [ { title: "权限资源管理", name: "permission", meta: { icon: "ion:list-outline", //需要校验权限 permission: "sys.per" }, path: "/sys/authority/permission", component: "/sys/authority/permission/index.vue" }, { title: "角色管理", name: "role", meta: { icon: "ion:people-outline", permission: "sys.role" }, path: "/sys/authority/role", component: "/sys/authority/role/index.vue" } ] }, { title: "用户管理", name: "user", meta: { icon: "ion:person-outline", permission: "sys.user" }, path: "/sys/authority/user", component: "/sys/authority/user/index.vue" } ] } ];
index.vue修改权限资源管理页面方便加权限
const permission = ref({ add: hasPermissions("sys:auth:per:add"), edit: hasPermissions("sys:auth:per:edit"), remove: hasPermissions("sys:auth:per:remove") }); 改成 const permission = ref({ add: hasPermissions("auth.add_permission"), edit: hasPermissions("auth.change_permission"), remove: hasPermissions("auth.delete_permission") });
PermissionViewSet添加相关方法
def create(self, request, *args, **kwargs): request.data["content_type"] = request.data["parentId"] request.data["name"] = request.data["title"] request.data["codename"] = request.data["permission"] return super().create(request, *args, **kwargs) @action(['POST'], detail=False, permission_classes=[permissions.IsAuthenticated]) def tree(self, request: Request): content_types = ContentType.objects.all() resdata = [] for c in content_types: permissions = Permission.objects.filter(content_type_id=c.id).all() plist = [] for p in permissions: plist.append({ "id": p.id, "parentId": c.id, "permission": p.codename, "title": p.name, "sort": 100 }) resdata.append({ "id": c.id, "parentId": -1, "permission": c.app_label, "title": c.model, "children": plist, "sort": 100 }) return Response({ "code": 0, "data": resdata })
api.ts
import { request } from "/src/api/service"; const apiPrefix = "/permissions/"; export async function GetList(query) { return request({ url: apiPrefix, method: "get", data: query }); } export async function GetTree() { return request({ url: apiPrefix + "tree/", method: "post" }); } export async function AddObj(obj) { return request({ url: apiPrefix, method: "post", data: obj }); } export async function UpdateObj(obj) { return request({ url: apiPrefix, method: "put", data: obj }); } export async function DelObj(id) { return request({ url: apiPrefix, method: "delete", params: { id } }); } export async function GetObj(id) { return request({ url: apiPrefix + "/info", method: "post", params: { id } }); }
crud.ts修改url
/sys/authority/permission/tree
改成
/permissions/tree/
这样权限就能添加了。
superuser授予一个用户这些权限用户就能浏览菜单,去除后会提示“对不起,您没有权限”
这样用户权限前后端联调完成。