RBAC、xadmin与Book系列多表群操作

RBAC、xadmin与Book系列多表群操作

一 RBAC-基于角色的访问控制

1 什么是RBAC

1.1 概念

-RBAC  是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

1.2 应用

# RBAC - Role-Based Access Control
# Django的 Auth组件 采用的认证规则就是RBAC

# 1)像专门做人员权限管理的系统(CRM系统)都是公司内部使用,所以数据量都在10w一下,一般效率要求也不是很高
# 2)用户量极大的常规项目,会分两种用户:前台用户(三大认证) 和 后台用户(RBAC来管理)

# 结论:没有特殊要求的Django项目可以直接采用Auth组件的权限六表,不需要自定义六个表,也不需要断开表关系,但可能需要自定义User表

1.3 前后台权限控制

# 1)后台用户对各表操作,是后台项目完成的,我们可以直接借助admin后台项目(Django自带的)
# 2)后期也可以用xadmin框架来做后台用户权限管理

# 3)前台用户的权限管理如何处理
#   定义了一堆数据接口的视图类,不同的登录用户是否能访问这些视图类,能就代表有权限,不能就代表无权限
#   前台用户权限用drf框架的 三大认证

2 Django的内置RBAC(六表)

2.1 rbac的表设计

# rbac的表设计
# 最初3张表
用户表  角色表   权限表

# 5张表
用户表  角色表   权限表
用户表和角色表是多对多,需要建立第三张表
角色和权限是多对多,需要建立第三张表

# 6 张表
用户表  角色表   权限表
用户表和角色表是多对多,需要建立第三张表
角色和权限是多对多,需要建立第三张表
用户和权限多对多,建立第三张表

2.2 django内置了rbac的6张表

auth_user:                  # 用户表,扩写
auth_group:                 # 角色表(分组表)
auth_permission:            # 权限表
auth_user_groups:           # 用户对角色的中间表
auth_group_permissions:     # 角色对权限的中间表
auth_user_user_permissions: # 用户对权限的中间表

2.3 权限三表

2.4 权限六表

二 Xadmin的使用

1 Xadmin及安装

xadmin是Django的第三方扩展,可使Django的admin站点使用更方便。

xadmin是django的一个app。

文档:https://xadmin.readthedocs.io/en/latest/index.html

1.1 安装

1.1.1 区分1.x和2.x
# -区分1.x和2.x
-1.x :pip3 install xadmin

-2.x :pip3 install git+git://github.com/sshwsfc/xadmin.git@django2
       pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2
1.1.2 django2.x+xadmin 2.x 环境搭建
1)-安装
2)-在app中注册
    'xadmin',
    'crispy_forms',
    'reversion',

3)-修改国际化
    # 修改使用中文界面
    LANGUAGE_CODE = 'zh-Hans'
    # 修改时区
    TIME_ZONE = 'Asia/Shanghai'

4)-数据迁移
    python3 manage.py makemigrations
    python3 manage.py migrate

5)-配置路由信息
    import xadmin
    xadmin.autodiscover()
    # version模块自动注册需要版本控制的 Model
    from xadmin.plugins import xversion
    xversion.register_models()

    urlpatterns = [
        path('xadmin/', xadmin.site.urls)
    ]
6)-创建超级用户
	python manage.py createsuperuser

7)-登录,访问地址,输入用户名密码进入
	http://127.0.0.1:8000/xadmin/

2 Xadmin的使用

2.1 站点的配置

-xadmin不再使用Django的admin.py,而是需要编写代码在adminx.py文件中。
-xadmin的站点管理类不用继承admin.ModelAdmin,而是直接继承object即可。
import xadmin
from xadmin import views

class BaseSetting(object):
    """xadmin的基本配置"""
    enable_themes = True  # 开启主题切换功能
    use_bootswatch = True

xadmin.site.register(views.BaseAdminView, BaseSetting)

class GlobalSettings(object):
    """xadmin的全局配置"""
    site_title = "路飞学城"          # 设置站点标题
    site_footer = "路飞学城有限公司"   # 设置站点的页脚
    menu_style = "accordion"        # 设置菜单折叠

xadmin.site.register(views.CommAdminView, GlobalSettings)

2.2 站点Model管理

	xadmin可以使用的页面样式控制基本与Django原生的admin一致。
  • list_display 控制列表展示的字段

    list_display = ['id', 'btitle', 'bread', 'bcomment']
    
  • search_fields 控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询

    search_fields = ['id','btitle']
    
  • list_filter 可以进行过滤操作的列,对于分类、性别、状态

    list_filter = ['is_delete']
    
  • ordering 默认排序的字段

  • readonly_fields 在编辑页面的只读字段

  • exclude 在编辑页面隐藏的字段

  • list_editable 在列表页可以快速直接编辑的字段

  • show_detail_fields 在列表页提供快速显示详情信息

  • refresh_times 指定列表页的定时刷新

    refresh_times = [5, 10,30,60]  # 设置允许后端管理人员按多长时间(秒)刷新页面
    
  • list_export 控制列表页导出数据的可选格式

    list_export = ('xls', 'xml', 'json')   list_export设置为None来禁用数据导出功能
    list_export_fields = ('id', 'btitle', 'bpub_date')
    
  • show_bookmarks 控制是否显示书签功能

    show_bookmarks = True
    
  • data_charts 控制显示图表的样式

    data_charts = {
            "order_amount": {
              'title': '图书发布日期表', 
              "x-field": "bpub_date", 
              "y-field": ('btitle',),
              "order": ('id',)
            },
        #    支持生成多个不同的图表
        #    "order_amount": {
        #      'title': '图书发布日期表', 
        #      "x-field": "bpub_date", 
        #      "y-field": ('btitle',),
        #      "order": ('id',)
        #    },
        }
    
    • title 控制图标名称
    • x-field 控制x轴字段
    • y-field 控制y轴字段,可以是多个值
    • order 控制默认排序
  • model_icon 控制菜单的图标

    class BookInfoAdmin(object):
        model_icon = 'fa fa-gift'
    
    xadmin.site.register(models.BookInfo, BookInfodmin)
    

2.3 使用(美化)

1)-写一个类
    class BookClass():
        # 该表展示的字段
        list_display = ['id', 'name', 'price','publish']
        # 按照这些字段搜索
        search_fields = ['id','name']
        # 按哪些字段过滤
        list_filter = ['is_delete']

        # 可以把数据导出成excel,json,xml格式
        list_export = ('xls', 'xml', 'json')
        # list_export设置为None来禁用数据导出功能
        list_export_fields = ('id', 'name', 'price')
        data_charts = {
            "order_amount": {
                'title': '随便写',
                "x-field": "price",
                "y-field": ('publish',),
                "order": ('id',)
            },

        }
2)-注册
    xadmin.site.register(models.Book,BookClass)

三 Book系列多表群操作

1 book,publish,author表关系及抽象表建立

# 注意:以后所有的数据删除,尽量用软删除,使用一个字段标志是否删除,而不是真正的从数据库中删除
	-好处: 1) 这样删除数据不会影响索引,不会导致索引失效
    	  2) 之前存的用户数据还在,以备以后使用
# 表模型如下
# 抽象出一个基表(不再数据库生成,abstract=True),只用来继承

class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        # 基表必须设置abstract,基表就是给普通Model类继承使用的,设置了abstract就不会完成数据库迁移完成建表
        abstract = True


class Book(BaseModel):
    name = models.CharField(max_length=16)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish', db_constraint=False, on_delete=models.DO_NOTHING)
    # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
    # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

    @property
    def publish_name(self):
        return self.publish.name
    @property
    def author_list(self):
        # ll=[]
        # for author in self.authors.all():
        #     ll.append({'name':author.name,'sex':author.get_sex_display()})
        return [{'name': author.name, 'sex': author.get_sex_display()} for author in self.authors.all()]


class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)


class Author(BaseModel):
    name = models.CharField(max_length=16)
    sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    # 有作者可以没有详情,删除作者,详情一定会被级联删除
    # 外键字段为正向查询字段,related_name是反向查询字段
    author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

2 book表单增群增

class BookView(APIView):
    def post(self, request, *args, **kwargs):
        if isinstance(request.data, dict):
            # 增一条
            ser = serializer.BookSerializer(data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            return APIResponse(data=ser.data)
        elif isinstance(request.data, list):
            # 增多条
            ser = serializer.BookSerializer(data=request.data, many=True)
            # 内部如何实现的?
            # many=True,ser不是BookSerializer对象,而是ListSerializer对象,套了一个个的BookSerializer
            print(type(ser))
            ser.is_valid(raise_exception=True)
            #
            from rest_framework.serializers import ListSerializer
            ser.save()  # ListSerializer的save
            return APIResponse(msg='增加%s条成功' % len(request.data))

3 book表单查群查

class BookView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            # 单查
            # 方式一
            # book=models.Book.objects.filter(id=pk).filter(is_delete=False).first()
            # if not book:
            #     raise Exception('要查询的不存在')

            # 方式二
            book = models.Book.objects.get(id=pk, is_delete=False)
            ser = serializer.BookSerializer(instance=book)

        else:
            # 查所有
            book_list = models.Book.objects.all().filter(is_delete=False)
            ser = serializer.BookSerializer(instance=book_list, many=True)
        return APIResponse(data=ser.data)

4 book表单改群改

class BookView(APIView):
    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            # 单条修改
            book = models.Book.objects.get(id=pk, is_delete=False)
            ser = serializer.BookSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            return APIResponse(msg='修改成功')
        else:
            # 分析:ListSerializer的update方法没有写,需要我们自己写
            from rest_framework.serializers import ListSerializer
            # pks=[item['id'] for item in request.data]

            # 如果不重写ListSerializer的update方法,这是存不进去的
            pks = []
            for item in request.data:
                pks.append(item['id'])
                item.pop('id')

            print(request.data)
            book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
            ser = serializer.BookSerializer(instance=book_list, data=request.data, many=True)
            print(type(ser))
            ser.is_valid(raise_exception=True)
            ser.save()
            return APIResponse(msg='修改%s条成功')

            # 你们能想到的方法
            # pks = []
            # for item in request.data:
            #     pks.append(item['id'])
            #     item.pop('id')
            # book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
            #
            # for i,book in enumerate(book_list):
            #     ser = serializer.BookSerializer(instance=book, data=request.data[i])
            #     ser.is_valid(raise_exception=True)
            #     ser.save()
            # return APIResponse(msg='修改%s条成功'%len(book_list))

5 book表的单删群删

class BookView(APIView):

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        pks = []
        if pk:
            # 单条删除
            # res=models.Book.objects.filter(id=pk).update(is_delete=True)
            # print(res)
            # return APIResponse(msg='删除成功')
            pks.append(pk)
        else:
            pks = request.data

        res = models.Book.objects.filter(id__in=pks, is_delete=False).update(is_delete=True)
        if res >= 1:
            return APIResponse(msg='删除%s条成功' % res)
        else:
            # raise Exception('没有要删除的数据')
            return APIResponse(code=999, msg='没有要删除的数据')

6 序列化类

from app01 import models


class ListBookSerializer(serializers.ListSerializer):
    # def create(self, validated_data):
    #     print('=======',validated_data)
    #     return '1'
    def update(self, instance, validated_data):
        print(instance) # book_list:是一堆图书对象
        print(validated_data) # 列表套字典,是要修改的数据

        return [self.child.update(book, validated_data[i]) for i, book in enumerate(instance)]


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        list_serializer_class=ListBookSerializer # 指定many=True的时候,生成的ListBookSerializer的对象了
        fields = ['name', 'price', 'publish', 'authors', 'publish_name', 'author_list']
        extra_kwargs = {
            'publish': {'write_only': True},
            'authors': {'write_only': True},
            'publish_name': {'read_only': True},
            'author_list': {'read_only': True},

        }

    # def create(self, validated_data):
    #     print(validated_data)

7 路由

path('books/', views.BookView.as_view()),
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
posted @ 2021-06-30 09:39  越关山  阅读(135)  评论(0)    收藏  举报